<?php
/**
 * @version 1.0
 * @author Jonathan Poelen <jo.link.noir@gmail.com>
 * @date 14-09-2010
 */
abstract class SW_Removable implements SW_Removable_Interface
{
	/** @var int : retourne une exception lorsque qu'on ajoute un type qui existe déjà*/
	const ADD_VALUE_NOT_REPLEACE = 1;

	/** @var int : ajoute la valeur même si le contrôleur n'existe pas*/
	const ADD_VALUE_NONEXISTENT_CHECK = 2;


	public $errorMessages = array(
		0 => 'Erreur Removable',
		1 => 'Paramètre(s) invalide(s)',
		2 => 'La type existe déjà, impossible de le remplacer',
		3 => 'La valeur ne passe pas le contrôleur'
	);


	/** @var array : {int|string => mixed, …}*/
	protected $values = array();

	/** @var array : {int|string => SW_Checker, …}*/
	protected $checkers = array();


	/** @var int */
	private $flag;


	/**
	 * @param SW_Move $move
	 * @param SW_Units $army
	 */
	abstract protected function _depose(SW_Move $move, SW_Units $army);


	/**
	 * @param int $flag = 0
	 */
	public function __construct($flag = 0)
	{
		$this->flag = $flag;
	}

	/**
	 * @return int
	 */
	public function getFlag()
	{
		return $this->flag;
	}

	/**
	 * @param int $flag
	 */
	public function setFlag($flag)
	{
		$this->flag = $flag;
	}

	/**
	 * @param int|string $type
	 * @param SW_Checker $checker
	 */
	public function addCheck($type, SW_Checker $checker)
	{
		$this->checkers[$type] = $checker;
	}

	/**
	 * @param int|string $type
	 */
	public function removeCheck($type)
	{
		if (isset($this->checkers[$type]))
			unset($this->checkers[$type]);
	}

	/**
	 * @param int|string $type
	 * @return bool
	 */
	public function existsCheck($type)
	{
		return isset($this->checkers[$type]);
	}

	/**
	 * @param SW_Move $move
	 * @param SW_Units $army
	 * @exception SW_Removable_Exception : code 3 ou celui retourné par checkDepose
	 */
	public function depose(SW_Move $move, SW_Units $army)
	{
		$check = $this->checkDepose($move, $army);
		if ($check === true || $check === 0)
			$this->_depose($move, $army);
		else
		{
			if (!$check || !isset($this->errorMessages[$check]))
				$check = 3;
			throw new SW_Removable_Exception($this->errorMessages[$check], $check);
		}
	}

	/**
	 * @param SW_Move $move
	 * @param SW_Units $army
	 * @return bool|int : si l'entier est supérieur à 0 alors il correspond à l'id de errorMessage
	 */
	protected function checkDepose(SW_Move $move, SW_Units $army)
	{
		return true;
	}

	/**
	 * @param int|string $type
	 * @param mixed $value
	 * @exception SW_Removable_Exception : code 2 ou 3
	 */
	public function add($type, $value)
	{
		if (isset($this->values[$type]) && $this->flag & self::ADD_VALUE_NOT_REPLEACE)
			throw new SW_Removable_Exception($this->errorMessages[2], 2);

		if (isset($this->checkers[$type]))
		{
			$check = $this->checkers[$type]->check($value);
			if (!($check === true || $check === 0))
			{
				if (!$check || !isset($this->errorMessages[$check]))
					$check = 3;
				throw new SW_Removable_Exception($this->errorMessages[$check], $check);
			}
		}
		else if (!($this->flag & self::ADD_VALUE_NONEXISTENT_CHECK))
			throw new SW_Removable_Exception($this->errorMessages[3], 3);

		$this->values[$type] = $value;
	}

	/**
	 * @return array : [type:string => mixed, …]
	 */
	public function gets()
	{
		return $this->values;
	}

	/**
	 * @param stdClass|string $type
	 * @return mixed : null si la valeur n'existe pas
	 */
	public function get($type)
	{
		return isset($this->values[$type]) ? $this->values[$type] : null;
	}

	/**
	 * @return bool
	 */
	public function isEmpty()
	{
		return empty($this->values);
	}

	/**
	 * @return bool
	 */
	public function exists($type)
	{
		return isset($this->values[$type]);
	}
}